package com.cardcaptorsakura.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * JWT认证过滤器
 * 拦截HTTP请求并验证JWT令牌
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final ObjectMapper objectMapper;

    // 不需要认证的路径
    private static final Set<String> PUBLIC_PATHS = Set.of(
        "/api/auth/login",
        "/api/auth/register",
        "/api/auth/refresh",
        "/api/auth/logout",
        "/api/health",
        "/api/public",
        "/swagger-ui",
        "/v3/api-docs",
        "/actuator"
    );

    // 需要特殊权限的路径
    private static final Map<String, Set<String>> PROTECTED_PATHS = Map.of(
        "/api/admin", Set.of("ROLE_ADMIN"),
        "/api/persona/switch", Set.of("ROLE_USER", "ROLE_VIP", "ROLE_ADMIN"),
        "/api/memory", Set.of("ROLE_USER", "ROLE_VIP", "ROLE_ADMIN"),
        "/api/chat", Set.of("ROLE_USER", "ROLE_VIP", "ROLE_ADMIN")
    );

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        
        String requestPath = request.getRequestURI();
        String method = request.getMethod();
        
        try {
            // 1. 检查是否是公开路径
            if (isPublicPath(requestPath)) {
                log.debug("Public path accessed: {}", requestPath);
                filterChain.doFilter(request, response);
                return;
            }
            
            // 2. 提取JWT令牌
            String token = extractTokenFromRequest(request);
            if (!StringUtils.hasText(token)) {
                log.debug("No JWT token found in request to: {}", requestPath);
                handleAuthenticationError(response, "MISSING_TOKEN", "JWT token is required");
                return;
            }
            
            // 3. 验证JWT令牌
            if (!jwtUtil.validateToken(token)) {
                log.debug("Invalid JWT token for request to: {}", requestPath);
                handleAuthenticationError(response, "INVALID_TOKEN", "JWT token is invalid or expired");
                return;
            }
            
            // 4. 解析令牌获取用户信息
            Claims claims = jwtUtil.getClaimsFromToken(token);
            String userId = claims.getSubject();
            String tokenType = claims.get("type", String.class);
            
            // 5. 检查令牌类型
            if (!isValidTokenType(tokenType, requestPath)) {
                log.debug("Invalid token type '{}' for request to: {}", tokenType, requestPath);
                handleAuthenticationError(response, "INVALID_TOKEN_TYPE", 
                    "Token type '" + tokenType + "' is not valid for this endpoint");
                return;
            }
            
            // 6. 创建认证对象
            UsernamePasswordAuthenticationToken authentication = createAuthentication(claims, request);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            
            // 7. 检查路径权限
            if (!hasRequiredPermission(requestPath, authentication)) {
                log.debug("Insufficient permissions for user '{}' to access: {}", userId, requestPath);
                handleAuthorizationError(response, "INSUFFICIENT_PERMISSIONS", 
                    "You don't have permission to access this resource");
                return;
            }
            
            // 8. 添加用户信息到请求头（供下游使用）
            addUserInfoToRequest(request, claims);
            
            log.debug("Successfully authenticated user '{}' for request to: {}", userId, requestPath);
            
            // 9. 继续过滤链
            filterChain.doFilter(request, response);
            
        } catch (Exception e) {
            log.error("Error during JWT authentication for request to: {}", requestPath, e);
            handleAuthenticationError(response, "AUTHENTICATION_ERROR", "Authentication failed");
        }
    }

    /**
     * 从请求中提取JWT令牌
     */
    private String extractTokenFromRequest(HttpServletRequest request) {
        // 1. 从Authorization头中提取
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        
        // 2. 从查询参数中提取（用于WebSocket等场景）
        String tokenParam = request.getParameter("token");
        if (StringUtils.hasText(tokenParam)) {
            return tokenParam;
        }
        
        // 3. 从Cookie中提取
        if (request.getCookies() != null) {
            for (var cookie : request.getCookies()) {
                if ("jwt_token".equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }
        
        return null;
    }

    /**
     * 检查是否是公开路径
     */
    private boolean isPublicPath(String requestPath) {
        return PUBLIC_PATHS.stream().anyMatch(requestPath::startsWith);
    }

    /**
     * 检查令牌类型是否有效
     */
    private boolean isValidTokenType(String tokenType, String requestPath) {
        if (tokenType == null) {
            return false;
        }
        
        switch (tokenType) {
            case "ACCESS":
                return true; // 访问令牌可以访问所有需要认证的端点
            case "REFRESH":
                return requestPath.startsWith("/api/auth/refresh"); // 刷新令牌只能用于刷新
            case "PERSONA_SWITCH":
                return requestPath.startsWith("/api/persona/switch"); // 人格切换令牌只能用于人格切换
            default:
                return false;
        }
    }

    /**
     * 创建认证对象
     */
    private UsernamePasswordAuthenticationToken createAuthentication(Claims claims, HttpServletRequest request) {
        String userId = claims.getSubject();
        String username = claims.get("username", String.class);
        String userType = claims.get("userType", String.class);
        String currentPersona = claims.get("currentPersona", String.class);
        
        // 解析角色
        List<String> roles = claims.get("roles", List.class);
        if (roles == null) {
            roles = List.of("ROLE_USER"); // 默认角色
        }
        
        List<SimpleGrantedAuthority> authorities = roles.stream()
            .map(SimpleGrantedAuthority::new)
            .collect(Collectors.toList());
        
        // 创建用户主体
        JwtUserPrincipal principal = new JwtUserPrincipal(
            userId, username, userType, currentPersona, authorities
        );
        
        UsernamePasswordAuthenticationToken authentication = 
            new UsernamePasswordAuthenticationToken(principal, null, authorities);
        
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        
        return authentication;
    }

    /**
     * 检查是否有必需的权限
     */
    private boolean hasRequiredPermission(String requestPath, UsernamePasswordAuthenticationToken authentication) {
        // 查找匹配的保护路径
        for (Map.Entry<String, Set<String>> entry : PROTECTED_PATHS.entrySet()) {
            if (requestPath.startsWith(entry.getKey())) {
                Set<String> requiredRoles = entry.getValue();
                Set<String> userRoles = authentication.getAuthorities().stream()
                    .map(authority -> authority.getAuthority())
                    .collect(Collectors.toSet());
                
                // 检查用户是否有任一必需角色
                return requiredRoles.stream().anyMatch(userRoles::contains);
            }
        }
        
        // 如果没有特殊要求，默认允许已认证用户访问
        return true;
    }

    /**
     * 添加用户信息到请求属性
     */
    private void addUserInfoToRequest(HttpServletRequest request, Claims claims) {
        request.setAttribute("userId", claims.getSubject());
        request.setAttribute("username", claims.get("username", String.class));
        request.setAttribute("userType", claims.get("userType", String.class));
        request.setAttribute("currentPersona", claims.get("currentPersona", String.class));
        request.setAttribute("roles", claims.get("roles", List.class));
        request.setAttribute("tokenType", claims.get("type", String.class));
    }

    /**
     * 处理认证错误
     */
    private void handleAuthenticationError(HttpServletResponse response, String errorCode, String message) 
            throws IOException {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("success", false);
        errorResponse.put("errorCode", errorCode);
        errorResponse.put("message", message);
        errorResponse.put("timestamp", System.currentTimeMillis());
        
        response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
    }

    /**
     * 处理授权错误
     */
    private void handleAuthorizationError(HttpServletResponse response, String errorCode, String message) 
            throws IOException {
        response.setStatus(HttpStatus.FORBIDDEN.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("success", false);
        errorResponse.put("errorCode", errorCode);
        errorResponse.put("message", message);
        errorResponse.put("timestamp", System.currentTimeMillis());
        
        response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
    }

    /**
     * JWT用户主体类
     */
    public static class JwtUserPrincipal {
        private final String userId;
        private final String username;
        private final String userType;
        private final String currentPersona;
        private final List<SimpleGrantedAuthority> authorities;

        public JwtUserPrincipal(String userId, String username, String userType, 
                               String currentPersona, List<SimpleGrantedAuthority> authorities) {
            this.userId = userId;
            this.username = username;
            this.userType = userType;
            this.currentPersona = currentPersona;
            this.authorities = authorities;
        }

        // Getters
        public String getUserId() { return userId; }
        public String getUsername() { return username; }
        public String getUserType() { return userType; }
        public String getCurrentPersona() { return currentPersona; }
        public List<SimpleGrantedAuthority> getAuthorities() { return authorities; }
        
        @Override
        public String toString() {
            return "JwtUserPrincipal{" +
                   "userId='" + userId + '\'' +
                   ", username='" + username + '\'' +
                   ", userType='" + userType + '\'' +
                   ", currentPersona='" + currentPersona + '\'' +
                   '}';
        }
    }

    /**
     * 获取当前认证用户信息的工具方法
     */
    public static JwtUserPrincipal getCurrentUser() {
        var authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.getPrincipal() instanceof JwtUserPrincipal) {
            return (JwtUserPrincipal) authentication.getPrincipal();
        }
        return null;
    }

    /**
     * 获取当前用户ID的工具方法
     */
    public static String getCurrentUserId() {
        JwtUserPrincipal user = getCurrentUser();
        return user != null ? user.getUserId() : null;
    }

    /**
     * 获取当前用户名的工具方法
     */
    public static String getCurrentUsername() {
        JwtUserPrincipal user = getCurrentUser();
        return user != null ? user.getUsername() : null;
    }

    /**
     * 获取当前用户类型的工具方法
     */
    public static String getCurrentUserType() {
        JwtUserPrincipal user = getCurrentUser();
        return user != null ? user.getUserType() : null;
    }

    /**
     * 获取当前人格的工具方法
     */
    public static String getCurrentPersona() {
        JwtUserPrincipal user = getCurrentUser();
        return user != null ? user.getCurrentPersona() : null;
    }

    /**
     * 检查当前用户是否有指定角色
     */
    public static boolean hasRole(String role) {
        var authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            return authentication.getAuthorities().stream()
                .anyMatch(authority -> authority.getAuthority().equals(role));
        }
        return false;
    }

    /**
     * 检查当前用户是否是管理员
     */
    public static boolean isAdmin() {
        return hasRole("ROLE_ADMIN");
    }

    /**
     * 检查当前用户是否是VIP
     */
    public static boolean isVip() {
        return hasRole("ROLE_VIP") || hasRole("ROLE_PREMIUM") || hasRole("ROLE_ADMIN");
    }
}